package org.shoukaiseki.common.cost;

import org.shoukaiseki.common.exception.MessageVirtualException;
import org.shoukaiseki.common.utils.CostMathUtils;
import org.shoukaiseki.common.utils.NumberUtils;

import java.math.BigDecimal;

import static org.shoukaiseki.common.cost.CostErpUtils.lineCostHalfUp;
import static org.shoukaiseki.common.utils.CostMathUtils.divideNullIsZero;
import static org.shoukaiseki.common.utils.CostMathUtils.multiplyNullIsZero;

/**
 * 计算范围内价格,不推荐使用超退功能
 * <pre>
 * 测试场景,入库
 * 退货时可以超出入库数量进行退货,超出时计算的单价为 overTransactionUnitCost,为null则默认采用 orderTotalCost/orderTotalQuantity
 * 入库时可以超出订单数量进行入库,超出时计算的单价为 overOrderUnitCost,为null则默认采用 orderTotalCost/orderTotalQuantity
 *
 * 假如: 一个入库单存在超退,然后再进行的入库操作
 *          那么入库时候首先把超退的部分按照超退成本进行入库,
 *          然后再按照订单成本进行入库
 *  适用场景,下单后换货时连同上次的货一同退货后进行更换,无需在两个采购单上进行,但是不适用与超退后不补,因为两个订单单价可能不一致,但是退货价格无法采用另一个订单的单价
 * </pre>
 */
public class CalculateScopeCost {

    private BigDecimal quantity;

    private BigDecimal scopeQuantity;

    private BigDecimal scopeLineCost;

    //剩余未消耗量
    private BigDecimal remainingUntreatedQuantity;

    //计算后的成本
    private BigDecimal calculateAfterCost;

    public CalculateScopeCost(BigDecimal quantity,BigDecimal scopeQuantity,BigDecimal scopeLineCost){
        this.quantity=quantity;
        this.scopeQuantity= scopeQuantity;
        this.scopeLineCost = scopeLineCost;
    }

    public CalculateScopeCost(BigDecimal quantity,BigDecimal scopeQuantity,BigDecimal scopeLineCost,boolean scopeNeagete){
        this.quantity=quantity;
        this.scopeQuantity=scopeNeagete?NumberUtils.negateHasNull(scopeQuantity):scopeQuantity;
        this.scopeLineCost = scopeNeagete?NumberUtils.negateHasNull(scopeLineCost):scopeLineCost;
    }

    /**
     * 计算退回时本范围内可用成本
     */
    public void  calculateCurrentReturnCost(){
        if(NumberUtils.isGreaterZero(quantity)){
            throw new MessageVirtualException("退回时的数量必须小于0");
        }
        //退回,如果
        if(NumberUtils.isLessZero(scopeQuantity)){
            remainingUntreatedQuantity=quantity;
            calculateAfterCost = BigDecimal.ZERO;
            return;
        }
        if(NumberUtils.isEquals(NumberUtils.negateHasNull(quantity),scopeQuantity)){
            remainingUntreatedQuantity=BigDecimal.ZERO;
            calculateAfterCost =NumberUtils.negateHasNull(scopeLineCost);
            return;
        }
        if (NumberUtils.isGreater(NumberUtils.negateHasNull(quantity),scopeQuantity)) {
            remainingUntreatedQuantity= CostMathUtils.addNullIsZero(quantity,scopeQuantity);
            calculateAfterCost =NumberUtils.negateHasNull(scopeLineCost);
            return;
        }

        BigDecimal scopeUnitCost = divideNullIsZero(scopeLineCost, scopeQuantity, 6);
        //计算成本,四舍五入
        calculateAfterCost =lineCostHalfUp(multiplyNullIsZero(quantity,scopeUnitCost));
        remainingUntreatedQuantity=BigDecimal.ZERO;
    }


    /**
     * 计算本次正向入库/发货成本
     * <pre>
     * 历史如果存在超退的,为负数的,需要在实例化时 negate=true,或者传入时使用整数传入
     * 入库时先按 超退的价格计算
     * 再按 采购单剩余未入库价格 入库
     * 最后超出部分使用 超入数量*超入单价
     * </pre>
     */
    public void calculateCurrentPositiveCost(){
        //退回,如果
        if(NumberUtils.isLessZero(scopeQuantity)){
            remainingUntreatedQuantity=quantity;
            calculateAfterCost = BigDecimal.ZERO;
            return;
        }
        if(NumberUtils.isEquals(quantity,scopeQuantity)){
            remainingUntreatedQuantity=BigDecimal.ZERO;
            calculateAfterCost =scopeLineCost;
            return;
        }
        if (NumberUtils.isGreater(quantity,scopeQuantity)) {
            remainingUntreatedQuantity= CostMathUtils.subtractNullIsZero(quantity,scopeQuantity);
            calculateAfterCost =scopeLineCost;
            return;
        }

        BigDecimal scopeUnitCost = divideNullIsZero(scopeLineCost, scopeQuantity, 6);
        calculateAfterCost =lineCostHalfUp(multiplyNullIsZero(quantity,scopeUnitCost));
        remainingUntreatedQuantity=BigDecimal.ZERO;
    }

    public BigDecimal getQuantity() {
        return quantity;
    }

    public void setQuantity(BigDecimal quantity) {
        this.quantity = quantity;
    }

    public BigDecimal getScopeQuantity() {
        return scopeQuantity;
    }

    public void setScopeQuantity(BigDecimal scopeQuantity) {
        this.scopeQuantity = scopeQuantity;
    }

    public BigDecimal getScopeLineCost() {
        return scopeLineCost;
    }

    public void setScopeLineCost(BigDecimal scopeLineCost) {
        this.scopeLineCost = scopeLineCost;
    }

    public BigDecimal getRemainingUntreatedQuantity() {
        return remainingUntreatedQuantity;
    }

    public BigDecimal getCalculateAfterCost() {
        return calculateAfterCost;
    }
}
